package com.iwdnb.gkgz.common.quota;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;

/**
 * 要实现CCI（Commodity Channel Index，商品通道指数）指标，我们需要利用给定的 StockDayInfo 类，该类包含了开盘价、收盘价、最高价和最低价等信息。CCI 的计算步骤如下：
 *
 * 计算每一个交易日的 TP（典型价格）。
 * 计算给定周期（如14天内）TP的简单移动平均值（SMA）。
 * 计算给定周期内的 TP 与其 SMA 之间的绝对差值的平均值。
 * 使用CCI公式计算CCI值。
 * 具体CCI公式如下：
 * (TP - SMA_{TP}) / (0.015 * MD)
 * 其中：
 *
 * TP = (最高价 + 最低价 + 收盘价) / 3
 * SMA_{TP} = TP的简单移动平均值
 * MD = TP 与其 SMA_{TP} 之间的绝对差值的平均值
 */
public class CCIIndicator {
    public static final String QUOTA_NAME = "cci";
    private static final Integer PERIOD = 14;

    public static void calculateCCI(List<StockDayData> stockDayDataList) {
        List<Double> list = doCalculateCCI(stockDayDataList, PERIOD);
        int size = stockDayDataList.size();
        for (int i = 0; i < size; i++) {
            StockDayData stockDayData = stockDayDataList.get(i);
            Double d = list.get(i);
            BigDecimal data = BigDecimalUtils.of(d);
            StockUtils.setStockQuota(stockDayData, QUOTA_NAME, data);
        }
    }

    private static List<Double> doCalculateCCI(List<StockDayData> stockDayDataList, int period) {
        List<Double> prices = stockDayDataList.stream().map(t -> t.getClosePrice().doubleValue()).collect(
            Collectors.toList());
        List<Double> cci = new ArrayList<>();
        if (prices.size() < period) {return cci;}

        for (int i = period; i <= prices.size(); i++) {
            List<Double> typicalPrices = prices.subList(i - period, i);
            double avgPrice = typicalPrices.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
            double meanDeviation = typicalPrices.stream().mapToDouble(price -> Math.abs(price - avgPrice)).average()
                .orElse(0.0);
            double cciValue = (typicalPrices.get(period - 1) - avgPrice) / (0.015 * meanDeviation); // 0.015是常数

            cci.add(cciValue);
        }
        // 前面的period个时间点没有RSI值，因此使用0填充
        for (int i = 0; i < period; i++) {
            cci.add(0, 0d);
        }
        return cci;
    }

    public static void main(String[] args) {
        // 示例数据，实际数据应该从文件或数据库获取
        List<StockDayData> stockData = Lists.newArrayList(
            createStockDayData("100", "105", "95", "102"),
            createStockDayData("102", "108", "98", "104"),
            createStockDayData("104", "110", "100", "106"),
            createStockDayData("106", "112", "101", "108"),
            createStockDayData("108", "115", "105", "111"),
            createStockDayData("111", "118", "107", "114"),
            createStockDayData("114", "120", "110", "117"),
            createStockDayData("117", "122", "115", "119"),
            createStockDayData("119", "124", "116", "120"),
            createStockDayData("120", "125", "118", "123"),
            createStockDayData("123", "128", "121", "127"),
            createStockDayData("127", "130", "124", "128"),
            createStockDayData("128", "133", "125", "132"),
            createStockDayData("132", "135", "128", "134"),
            createStockDayData("134", "137", "130", "136")
        );

        int period = 14; // 计算14周期CCI
        doCalculateCCI(stockData, period);
    }

    private static StockDayData createStockDayData(String open, String max, String min, String close) {
        StockDayData stockDayInfo = new StockDayData();
        stockDayInfo.setOpenPrice(new BigDecimal(open));
        stockDayInfo.setMaxPrice(new BigDecimal(max));
        stockDayInfo.setMinPrice(new BigDecimal(min));
        stockDayInfo.setClosePrice(new BigDecimal(close));
        return stockDayInfo;
    }
}

